/*
 * @Author: your name
 * @Date: 2021-09-21 14:33:27
 * @LastEditTime: 2021-09-24 21:33:12
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \threadpool\threadpool.h
 */
#ifndef __THREADPOOL_H__
#define __THREADPOOL_H__

#include <atomic>
#include <thread>
#include <functional>
#include <vector>
#include <queue>
#include <condition_variable>
#include <mutex>
#include <future>
#include <stdexcept>

#define MAX_NUMS 16

namespace hyf
{
    class ThreadPool
    {
    private:
        enum ThreadNums
        {
            maxNums = MAX_NUMS,
        };

    private:
        using Task = std::function<void()>;

    public:
        ThreadPool(unsigned int size = 4);
        ~ThreadPool();
        void AddThread(unsigned int num);
        int IdlCount() const;
        bool IsRun() const;
        template <typename F, typename... Args>
        auto commit(F &&f, Args &&...args) -> std::future<decltype(f(args...))>;
        template <typename F>
        auto commitCallable(F &&f) -> std::future<decltype(f())>;
        void push(const Task &task);

    private:
        std::vector<std::thread> m_pool;
        std::queue<Task> m_tasks;
        std::mutex m_mutex;
        std::condition_variable m_cv;
        std::atomic<bool> m_isRun;
        std::atomic<int> m_idlNum;
    };

    template <typename F, typename... Args>
    auto ThreadPool::commit(F &&f, Args &&...args) -> std::future<decltype(f(args...))>
    {
        if (!m_isRun)
            throw std::runtime_error("commit on threadpool is stopped.");

        using RetType = decltype(f(args...));

        auto task = std::make_shared<std::packaged_task<RetType()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        std::future<RetType> future = task->get_future();
        push([task]
             { (*task)(); });

        return future;
    }

    template <typename F>
    auto ThreadPool::commitCallable(F &&f) -> std::future<decltype(f())>
    {
        if (!m_isRun)
            throw std::runtime_error("commit on threadpool is stopped.");

        using RetType = decltype(f());

        auto task = std::make_shared<std::packaged_task<RetType()>>(std::bind(std::forward<F>(f)));
        std::future<RetType> future = task->get_future();
        push([task]
             { (*task)(); });

        return future;
    }

}

#endif
